#ifndef LJLIST_H
#define LJLIST_H

template<typename T>
class LJNode
{
	T m_Data;
	T* m_pNext;
public:
	LJNode(const LJNode *next, const T& data);
	LJNode():m_pNext(NULL){}
	~LJNode(void){}

	void SetData(const T& data);
	const T Data() const;
	T Data();

	void SetNext(const LJNode<T> *next);
	const LJNode<T>* Next() const;
	LJNode<T>* Next();
};

template<typename T>
LJNode<T>::LJNode(const LJNode *next, const T& data)
{
	m_Data = data;
	m_pNext = next;
}

template<typename T>
void LJNode<T>::SetData(const T& data)
{
	m_Data = data;
}

template<typename T>
const T LJNode<T>::Data() const
{
	return m_Data;
}

template<typename T>
T LJNode<T>::Data()
{
	returm m_Data;
}

template<typename T>
void LJNode<T>::SetNext(const LJNode<T> *next)
{
	m_pNext = next;
}

template<typename T>
const LJNode<T>* LJNode<T>::Next() const
{
	return m_pNext;
}
template<typename T>
LJNode<T>* LJNode<T>::Next()
{
	return m_pNext;
}
/*
 * template List
 */
template <typename T>
class LJList
{
	LJNode<T> *m_pHead;
	LJNode<T> *m_pTail;
	unsigned int m_nSize;
public:
	LJList():m_pHead(NULL), m_pTail(NULL), m_nSize(0){}
	~LJList(void);
	// insert after
	bool Insert(LJNode<T> *elem, const T& data);
	// delete
	bool Delete(LJNode<T> *elem);
	// get size
	int Size() const { return m_nSize; }
	// cleanup
	void CleanUp();
	// Head
	LJNode<T>* Head() const { return m_pHead; }
	// Tail
	LJNode<T>* Tail() const { return m_pTail; }

	class iterator;
	friend class iterator;
	class iterator
	{
		LJList<T> &m_List;
		LJNode<T> *m_Cur;
	public:
		// initialized by pointing to head
		iterator(LJList<T>& list):m_List(list), m_Cur(list.m_pHead){}
		// initialized by pointing to tail
		iterator(LJList<T>& list, bool):m_List(list), m_Cur(list.m_pTail){}
		// dereference
		LJNode<T>* operator*() const { return m_Cur; }
		// prefix form
		void operator++() const { m_Cur = m_Cur->Next(); }
		// postfix form
		void operator++(int) const { m_Cur = m_Cur->Next(); }
		// ==
		bool operator==(const iterator& it) const { return m_Cur == it.m_Cur; }
		// != 
		bool operator!=(const iterator& it) const { return m_Cur != it.m_Cur; }
	};

	iterator begin() const {return iterator(*this);}
	iterator end() const { return iterator(*this, true); }
};
/*
 * Destructor
 */
template <typename T>
LJList<T>::~LJList(void)
{
	CleanUp();
}

template <typename T>
bool LJList<T>::Insert(LJNode<T> *elem, const T& data)
{
	LJNode<T> *newElem  = new LJNode<T>(NULL, data);
	// insert head
	if(elem == m_pHead || elem == NULL)
	{
		newElem->SetNext(m_pHead);
		m_pHead = newElem;
		if(m_pHead->Next() == NULL)
		{
			m_pTail = m_pHead;
		}
		m_nSize++;
		return true;
	}

	LJNode<T> *curPos = m_pHead;
	while(curPos)
	{
		if(curPos == elem)
		{
			newElem->SetNext(elem->Next());
			elem->SetNext(newElem);
			if(newElem->Next() == NULL)
			{
				m_pTail = newElem;
			}
			m_nSize++;
			return true;
		}

		curPos = curPos->Next();
	}
	delete newElem;
	return false;
}

template <typename T>
bool LJList<T>::Delete(LJNode<T> *elem)
{
	if(!m_pHead || !elem)
	{
		return false;
	}
	if(elem == m_pHead)
	{
		m_pHead = m_pHead->Next();
		delete elem;
		if(!m_pHead)
		{
			m_pTail = NULL;
		}
		--m_nSize;
		return true;
	}

	LJNode<T> *node = m_pHead;
	while(node)
	{
		LJNode<T> *next = node->Next();
		if(next == elem)
		{
			next->SetNext(elem->Next());
			delete elem;
			if(!next->Next())
			{
				m_pTail = next;
			}
			--m_nSize;
			return true;
		}
		node = next;
	}

	return false;
}

template <typename T>
void LJList<T>::CleanUp()
{
	LJNode<T> *curPos = m_pHead;
	while(curPos)
	{
		LJNode<T> *next = curPos->Next();
		delete curPos;
		curPos = next;
	}
}

#endif